import * as effectTypes from "./effectTypes";
// 取消逻辑 1
const TASK_CANCEL = "TASK_CANCEL";

// 所有的迭代器都会有一个Symbol.iterator属性，而它是一个函数
const isIterator = (value) => typeof value[Symbol.iterator] === "function";
/**
 * 开始运行一个saga
 * @param {*} env 环境 channel dispatch
 * @param {*} saga
 * @param {*} callback 主 saga 内部有 分支 saga 会阻塞，要等 分支saga 执行完， 执行 主 saga next 也就是 callback 才不会阻塞
 *
 *
 * next 执行了 才回到 下一个 yield
 *
 */
function runSaga(env, saga, callback) {
  // 取消逻辑 2
  const task = { cancel: () => next(TASK_CANCEL) };
  const { channel, dispatch } = env;
  // 此处要做兼容处理，判断saga参数是不是一个迭代器，如果是直接用，如果不用执行后使用
  const it = isIterator(saga) ? saga : saga();
  // isError 有值 ，表示 val 是 error 数据
  function next(val, isError) {
    let result;
    if (isError) {
      // 针对 node 回调函数式的函数   next(err, true);
      result = it.throw(val);
    } else if (val === TASK_CANCEL) {
      // 取消逻辑 5
      //让此saga直接结束掉
      result = it.return(val);
    } else {
      result = it.next(val);
    }
    //取出第一个effect的value值
    const { value: effect, done } = result;
    //如果此saga尚未完成
    if (!done) {
      //  1.  如果此effect身上有Symbol.iterator函数属性的话, 是迭代器
      if (isIterator(effect)) {
        // 1-1. 自动开启执行此effect对应的saga 分支 迭代器
        runSaga(env, effect, next);
        //当前的 主 saga并不会阻塞，会继续向下执行
        //在原版代码中是需要等待上面的saga执行完了才可以继续向下执行，不能直接调用next
        //next();//TODO
      } else if (effect instanceof Promise) {
        //   2. Promise 成功了， 才往下走
        effect.then(next);
      } else {
        // 3. effect.type 判断
        switch (effect.type) {
          case effectTypes.TAKE:
            //监听有人向仓库派发某个动作，当派发那个动作后才会继续执行next,执行当前的saga
            channel.once(effect.actionType, (action) => next(action));
            break;
          case effectTypes.PUT:
            //要向仓库派发动作
            dispatch(effect.action);
            //派发动作不会阻塞当前的saga,会继续向后执行
            next();
            break;
          case effectTypes.FORK:
            //如果这是一个fork指令 的话，开始执行这个fork里面的saga
            let forkTask = runSaga(env, effect.saga(...effect.args));
            //而执行里面的saga并不阻塞当前的saga继续向下执行

            //不希望这个saga阻塞当前的saga
            //而是希望这个saga开启后，当前的saga自动向下执行
            //yield workerSaga();
            //这样写相关于开启了一个新的子进程去执行workerSaga,而不会阻塞当前的saga继续向下执行
            next(forkTask);
            break;
          case effectTypes.CALL:
            //传递参数并执行此函数，并等待promise完成，完成后接着调用next
            effect
              .func(...effect.args)
              .then(next, (error) => next(error, true));
            break;
          case effectTypes.CPS:
            effect.func(...effect.args, (err, data) => {
              if (err) {
                // isError 有值 ，表示 val 是 error 数据
                next(err, true);
              } else {
                next(data);
              }
            });
            break;
          case effectTypes.ALL:
            const { iterators } = effect;
            let result = [];
            let completedCount = 0;
            iterators.forEach((iterator, index) => {
              runSaga(env, iterator, (data) => {
                result[index] = data;
                if (++completedCount === iterators.length) {
                  next(result);
                }
              });
            });
            break;
          case effectTypes.CANCEL:
            // 取消逻辑 4
            effect.task.cancel();
            next();
            break;
          default:
            break;
        }
      }
    } else {
      // 主 saga 内部有 分支 saga 会阻塞，要等 分支saga 执行完， 执行 主 saga next 也就是 callback 才不会阻塞
      callback?.(effect);
    }
  }
  next();
  // 取消逻辑 3
  return task;
}
export default runSaga;
